package com.guang.lock.selfdefinelock;

import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @Description 自己来定义实现一把锁
 *
 *      CAS不会发生内核态的切换，所以性能层面上比较高
 *
 *      大的前提：
 *          1、锁的标识，当前锁是用state来进行标识的；
 *          2、加锁、解锁方法
 *          3、如果锁是自由状态，加锁成功的话需要让其返回加锁成功状态；
                但是有一个现象，对于外部线程来说，不知道锁是否是自由状态
                那么就意味着，外部线程来访问锁的时候，就分为了两种情况：被其他线程持有锁 或者 是当前锁是自由状态
                那么对于多线程来说：t1,t2,t3,t4
                如果当前锁被t1持有，t2,t3,t4线程因为持有不到锁，而去自旋，但是此时，如果t5线程进入，而t1线程又刚刚好将锁释放掉
                加入被t5持有了，那么对于t2,t3,t4线程来说，是不公平的，因为已经自旋了这么久了。因为外部的线程到来而导致当前线程无法使用
                所以需要考虑到公平和非公平的条件
                如果是公平锁，那么线程也应该等着前面的t2,t3,t4线程执行完之后，然后轮到t5；如果是不公平的，那么就无所谓

                但是如果是多线程条件下，自旋是很消耗CPU资源的。所以为了避免CPU资源的消耗，应该考虑让线程阻塞。那么阻塞应该要被唤醒，
                那么唤醒，那么就应该考虑到应该使用容器来盛放多线程，在锁释放之后，将线程再唤醒
 *
 *          4、如果锁不是自由状态，则让其加锁失败。所谓的加锁失败，是让其线程陷入到阻塞中或者是自旋
 *          5、解锁方法原则：释放锁的同时还需要唤醒其他线程（当前锁中使用的是自旋，所以不需要唤醒）
 *
 *  当前锁的缺点：
 *      1、不支持重入；
 *      2、不支持公平锁；
 *      3、拿不到锁的线程没有记录；
 *      4、拿不到锁的线程；因为自旋比较消耗性能；
 *      5、没有记录当前持有锁的线程
 *
 * @Author liguang
 * @Date 2022/10/20/14:14
 */
public class LigLock {

    private final static Logger logger = LoggerFactory.getLogger(LigLock.class);

    //标识---加锁成功=1  如果自由状态=0
    volatile int state=0;

    //unsafe 对象，因为cas操作需要这个对象
    static Unsafe unsafe;

    //state这个字段在ShadowLock对象当中的内存偏移量
    private static long stateOffset;

    /**
     * 1、获取unsafe对象，这个对象的获取稍微有点复杂，目前不需要关心
     * 2、通过unsafe对象得到state属性在当前对象当的偏移地址
     */
    static {
        try {
            unsafe = getUnSafe();
            stateOffset = unsafe.objectFieldOffset(LigLock.class.getDeclaredField("state"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //加锁方法
    @SneakyThrows
    public void lock(){
        //compareAndSet(0,1)  当某个属性（state）他的值如果为0 则修改1

        //效率高 cas 无锁 不会发生内核态切换

        //t1 执行 compareAndSet(0,1) before state =0；after  true ---->state=1
        //t1 执行 compareAndSet(0,1) before state =1

        // 这里保证了原子性操作
        while(!compareAndSet(0,1)){
            //加锁失败
            logger.info("当前线程{}在等待获取得到锁",Thread.currentThread().getName());
            Thread.sleep(10L);
        }
        // 如果成功，则进行赋值
        Thread currentThread = Thread.currentThread();
        logger.info("当前线程获取得到了锁，当前线程的名称是：{}",currentThread.getName());
    }

    public void unlock(){
        //思考这里为什么不要cas? 因为这里不需要来进行只有已经持有锁了的线程才可以操作
        state=0;
        logger.info("当前线程释放了锁，当前线程的名称是：{}",Thread.currentThread().getName());
    }


    //获取unsafe对象
    private static Unsafe getUnSafe() throws NoSuchFieldException, IllegalAccessException {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        return (Unsafe) theUnsafe.get(null);
    }

    //cas 修改state的值
    //expect预期值，如果相同则把state的值改成x 原子操作
    private boolean compareAndSet(int expect,int x){
        //stateOffset 一个地址 【state属性的地址】
        return unsafe.compareAndSwapInt(this, stateOffset, expect, x);
    };


}
